using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL.Fabric
{
    public class ProgramROMRAMBlock : Block
    {
        ProgramROMRAM oProg;
        ProgramTable oROMProgram;
        ProgramTable oRAMProgram;

        int iPCWidth = 0;
        int iRAMOffset = 0;
        int iRAMDepth = 0;
        int iConstWidth = 0;

        public ProgramROMRAMBlock(string psName, MicroCoreTable poOpTable, string psROMFilePath, string psRAMFilePath, int piRAMOffset, int piRAMDepth, int piConstWidth)
            : base(psName, "PROG_RAM")
        {
            iRAMOffset = piRAMOffset;
            iRAMDepth = piRAMDepth;
            iConstWidth = piConstWidth;

            oROMProgram = new ProgramTable(poOpTable);
            oROMProgram.CompileFileToROM(psROMFilePath);

            oRAMProgram = new ProgramTable(poOpTable,piRAMOffset);
            oRAMProgram.CompileFileToRAM(psRAMFilePath);

            iPCWidth = Conversion.MinBitWidth(piRAMOffset + piRAMDepth - 1);

            this.CreateInput("PC", iPCWidth);
            this.CreateOutput("CONST", piConstWidth);
            this.CreateOutput("IR", oROMProgram.IRWidth);
            
            this.CreateInput("CONST", piConstWidth);
            this.CreateInput("IR", oROMProgram.IRWidth);
            this.CreateInput("ADDRESS", iPCWidth);
            this.CreateControlInput("PROGRAM");
        }

        internal override void GenerateStructure()
        {
            oProg = new ProgramROMRAM(ParentComponent, Name + "_prog_rom_ram", oROMProgram, oRAMProgram, iRAMOffset, iRAMDepth, iConstWidth, Clock);


            NodeVector nvPC = ParentComponent.CreateNodeVector(Name + "_pc", Inputs["PC"].Width);
            Inputs["PC"].LinkNodeVector(nvPC);

            NodeVector nvConst = ParentComponent.CreateNodeVector(Name + "_const", Outputs["CONST"].Width);
            Outputs["CONST"].LinkNodeVector(nvConst);

            NodeVector nvIR = ParentComponent.CreateNodeVector(Name + "_ir", Outputs["IR"].Width);
            Outputs["IR"].LinkNodeVector(nvIR);


            NodeVector nvConstIn = ParentComponent.CreateNodeVector(Name + "_const_in", iConstWidth);
            Inputs["CONST"].LinkNodeVector(nvConstIn);

            NodeVector nvIRIn = ParentComponent.CreateNodeVector(Name + "_ir_in", oRAMProgram.IRWidth);
            Inputs["IR"].LinkNodeVector(nvIRIn);

            NodeVector nvProg = ParentComponent.CreateNodeVector(Name + "_prog",1);
            ControlInputs["PROGRAM"].LinkNodeVector(nvProg);

            NodeVector nvAddress = ParentComponent.CreateNodeVector(Name + "_addr", iPCWidth);
            Inputs["ADDRESS"].LinkNodeVector(nvAddress);


            //iPCWidth oROMProgram.IRWidth + piConstWidth);


            oProg.I_PC.Join(Inputs["PC"].NodeVector);
            oProg.O_CONST.Join(Outputs["CONST"].NodeVector);
            oProg.O_IR.Join(Outputs["IR"].NodeVector);

            oProg.I_ADDRESS.Join(nvAddress);
            oProg.I_WRITE_DATA_CONST.Join(nvConstIn);
            oProg.I_WRITE_DATA_IR.Join(nvIRIn);
            oProg.I_WRITE.Join(nvProg);
        }

        public override void AddSignalViews()
        {
            oProg.AddSignalViews();
        }

        public void SignalDisplay(HardwareScript poScript, int piClockTime)
        {
            oProg.SignalDisplay(poScript, piClockTime);
        }

        /*public bool WriteRAMToFPGA()
        {
            return oProg.WriteRAMToFPGA();
        }*/

        public string GetProgramText()
        {
            return oProg.FileText;
        }

        public Dictionary<int, int> GetProgramAddressToLine()
        {
            return oProg.AddressToLine;
        }

        public List<int> GetBreakpoints()
        {
            return oProg.Breakpoints;
        }

        /*public bool StartRAMOnFPGA()
        {
            FPGABlock oBlock = new FPGABlock();
            oBlock.StartProgramRAM();
            return IOInterface.PostFPGA(oBlock);
        }*/

        public void LoadRAMProgram()
        {
            oProg.LoadRAMProgram();
        }
    }
}
